package airacrobats.util;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3f;

/**
 * Util class.
 */
public class StageWriter
{
    /**
     * @param args Main args.
     * @throws Exception Thrown if an I/O error occurs.
     */
    public static void main(String[] args) throws IOException
    {
        File file = new File("data/ef/dat/Stages.dat");

        DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream(file));

        createZeroStage(dataOutputStream);
        createFirstStage(dataOutputStream);
        createSecondStage(dataOutputStream);
        
        dataOutputStream.close();
    }

    /**
     * Creates the free flight.
     * @param dataOutputStream The output stream.
     * @throws IOException Thrown if an error occurs.
     */
    private static void createZeroStage(DataOutputStream dataOutputStream) throws IOException
    {
        // Writes 'name'.
        String name = "Free flight";
        dataOutputStream.writeChar(name.getBytes(Charset.forName("UTF-8")).length);
        dataOutputStream.write(name.getBytes(Charset.forName("UTF-8")));
        // Writes 'description'.
        String description = "On this stage, you fly the way you want to, "
                + "getting experience with the controls of the game and learning the limitations of your "
                + "airplane.";
        dataOutputStream.writeChar(description.getBytes(Charset.forName("UTF-8")).length);
        dataOutputStream.write(description.getBytes(Charset.forName("UTF-8")));
        // Writes 'k routes'.
        dataOutputStream.writeByte(1);
        // FIRST ROUTE.
        writeRoute(new Point3f(-200.0f, 20.0f, 0.0f),
                new Vector3f(97.0f, 0.0f, 0.0f),
                new ArrayList<int[][]>(), 0,
                false,
                dataOutputStream);
        // Writes 'needed score'.
        dataOutputStream.writeInt(30000);
        // Writes 'duration'.
        dataOutputStream.writeChar(60);
        // Writes number of radio sounds.
        dataOutputStream.writeChar(0);
    }

    /**
     * Creates the first stage.
     * @param dataOutputStream The output stream.
     * @throws IOException Thrown if an error occurs.
     */
    private static void createFirstStage(DataOutputStream dataOutputStream) throws IOException
    {
        List<int[][]> movements = null;
        
        // Writes 'name'.
        String name = "Espelhão";
        dataOutputStream.writeChar(name.getBytes(Charset.forName("UTF-8")).length);
        dataOutputStream.write(name.getBytes(Charset.forName("UTF-8")));
        // Writes 'description'.
        String description = "Six airplanes fly straight, with three of them upside-down, above the "
                + "other three.";
        dataOutputStream.writeChar(description.getBytes(Charset.forName("UTF-8")).length);
        dataOutputStream.write(description.getBytes(Charset.forName("UTF-8")));
        // Writes 'k routes'.
        dataOutputStream.writeByte(6);
        // ROUTE 0.
        movements = new ArrayList<int[][]>();
        movements.add(new int[][] { 
            new int[] { 1700, 0, 0, 100, 1 },
            new int[] { 5000, 100, 0, 100, 1 },
            new int[] { 7500, 0, 0, 100, 1 },
            new int[] { 26000, 100, 0, 100, 1 },
            new int[] { 28600, 0, 0, 100, 1 },
            new int[] { 31500, 0, 0, 100, 0 }});
        writeRoute(new Point3f(-200.0f, 20.0f, 0.0f),
                new Vector3f(97.0f, 0.0f, 0.0f),
                movements, 6,
                false,
                dataOutputStream);
        // ROUTE 1.
        movements = new ArrayList<int[][]>();
        movements.add(new int[][] {
            new int[] { 1700, 0, 0, 100, 1 },
            new int[] { 5000, 100, 0, 100, 1 },
            new int[] { 7500, 0, 0, 100, 1 },
            new int[] { 26000, 100, 0, 100, 1 },
            new int[] { 28600, 0, 0, 100, 1 },
            new int[] { 31500, 0, 0, 100, 0 }});
        writeRoute(new Point3f(-200.0f, 20.0f, 1.5f),
                new Vector3f(97.0f, 0.0f, 0.0f),
                movements, 6,
                false,
                dataOutputStream);
        // ROUTE 2.
        movements = new ArrayList<int[][]>();
        movements.add(new int[][] {
            new int[] { 1700, 0, 0, 100, 1 },
            new int[] { 5000, 100, 0, 100, 1 },
            new int[] { 7500, 0, 0, 100, 1 },
            new int[] { 26000, 100, 0, 100, 1 },
            new int[] { 28600, 0, 0, 100, 1 },
            new int[] { 31500, 0, 0, 100, 0 }});
        writeRoute(new Point3f(-200.0f, 20.0f, -1.5f),
                new Vector3f(97.0f, 0.0f, 0.0f),
                movements, 6,
                false,
                dataOutputStream);
        // ROUTE 3.
        writeRoute(new Point3f(-200.0f, 19.0f, 0.0f),
                new Vector3f(97.0f, 0.0f, 0.0f),
                new ArrayList<int[][]>(), 0,
                false,
                dataOutputStream);
        // ROUTE 4.
        writeRoute(new Point3f(-200.0f, 19.0f, 1.5f),
                new Vector3f(97.0f, 0.0f, 0.0f),
                new ArrayList<int[][]>(), 0,
                false,
                dataOutputStream);
        // ROUTE 5.
        writeRoute(new Point3f(-200.0f, 19.0f, -1.5f),
                new Vector3f(97.0f, 0.0f, 0.0f),
                new ArrayList<int[][]>(), 0,
                false,
                dataOutputStream);
        // Writes 'needed score'.
        dataOutputStream.writeInt(39000);
        // Writes 'duration'.
        dataOutputStream.writeChar(40);

        // Writes number of radio sounds.
        dataOutputStream.writeChar(4);

        // Radio sounds.
        dataOutputStream.writeChar(0);
        dataOutputStream.writeInt(500);
        // -
        dataOutputStream.writeChar(1);
        dataOutputStream.writeInt(3600);
        // -
        dataOutputStream.writeChar(2);
        dataOutputStream.writeInt(25000);
        // -
        dataOutputStream.writeChar(3);
        dataOutputStream.writeInt(30000);
    }

    /**
     * Creates the second stage.
     * @param dataOutputStream The output stream.
     * @throws IOException Thrown if an error occurs.
     */
    private static void createSecondStage(DataOutputStream dataOutputStream) throws IOException
    {
        List<int[][]> movements = null;

        // Writes 'name'.
        String name = "Break";
        dataOutputStream.writeChar(name.getBytes(Charset.forName("UTF-8")).length);
        dataOutputStream.write(name.getBytes(Charset.forName("UTF-8")));
        // Writes 'description'.
        String description = "After a loop and a curve, with formation change between these movements,"
                + " all the seven airplanes are flying on the front of the  public. That is when a "
                + "beautiful break, like a peacock tail is performed.";
        dataOutputStream.writeChar(description.getBytes(Charset.forName("UTF-8")).length);
        dataOutputStream.write(description.getBytes(Charset.forName("UTF-8")));
        // Writes 'k routes'.
        dataOutputStream.writeByte(7);
        // ROUTE 0.
        movements = new ArrayList<int[][]>();
        movements.add(new int[][] { 
            new int[] { 1700, 0, 0, 100, 1 },
            new int[] { 3000, 0, -100, 100, 1 },
            new int[] { 4000, 0, 0, 100, 1 },
            new int[] { 7000, 0, 100, 100, 1 },
            new int[] { 8500, 0, 0, 100, 1 },
            new int[] { 9000, 0, 100, 100, 1 },
            new int[] { 14000, 0, 100, 100, 1 },
            new int[] { 15000, 0, 0, 100, 0 }});
        writeRoute(new Point3f(-152.5f, 30.0f, 0.0f),
                new Vector3f(97.0f, 0.0f, 0.0f),
                movements, 8,
                false,
                dataOutputStream);
        // ROUTE 1.
        movements = new ArrayList<int[][]>();
        movements.add(new int[][] {
            new int[] { 1700, 0, 0, 100, 1 },
            new int[] { 3000, 0, -100, 100, 1 },
            new int[] { 4000, 0, 0, 100, 1 },
            new int[] { 7000, 0, 100, 100, 1 },
            new int[] { 8500, 0, 0, 100, 1 },
            new int[] { 9000, -10, 100, 100, 1 },
            new int[] { 14000, -10, 100, 100, 1},
            new int[] { 15000, 0, 0, 100, 0 }});
        writeRoute(new Point3f(-154.0f, 30.0f, -1.0f),
                new Vector3f(97.0f, 0.0f, 0.0f),
                movements, 8,
                false,
                dataOutputStream);
        // ROUTE 2.
        movements = new ArrayList<int[][]>();
        movements.add(new int[][] {
            new int[] { 1700, 0, 0, 100, 1 },
            new int[] { 3000, 0, -100, 100, 1 },
            new int[] { 4000, 0, 0, 100, 1 },
            new int[] { 7000, 0, 100, 100, 1 },
            new int[] { 8500, 0, 0, 100, 1 },
            new int[] { 9000, 10, 100, 100, 1 },
            new int[] { 14000, 10, 100, 100, 1 },
            new int[] { 15000, 0, 0, 100, 0 }});
        writeRoute(new Point3f(-154.0f, 30.0f, 1.0f),
                new Vector3f(97.0f, 0.0f, 0.0f),
                movements, 8,
                false,
                dataOutputStream);
        // ROUTE 3.
        movements = new ArrayList<int[][]>();
        movements.add(new int[][] {
            new int[] { 1700, 0, 0, 100, 1 },
            new int[] { 3000, 0, -100, 100, 1 },
            new int[] { 4000, 0, 0, 100, 1 },
            new int[] { 7000, 0, 100, 100, 1 },
            new int[] { 8500, 0, 0, 100, 1 },
            new int[] { 9000, -35, 100, 100, 1 },
            new int[] { 12000, 0, 0, 100, 1 },
            new int[] { 14000, 0, 0, 100, 0 }});
        writeRoute(new Point3f(-155.5f, 30.0f, -2.0f),
                new Vector3f(97.0f, 0.0f, 0.0f),
                movements, 8,
                false,
                dataOutputStream);
        // ROUTE 4.
        movements = new ArrayList<int[][]>();
        movements.add(new int[][] {
            new int[] { 1700, 0, 0, 100, 1 },
            new int[] { 3000, 0, -100, 100, 1 },
            new int[] { 4000, 0, 0, 100, 1 },
            new int[] { 7000, 0, 100, 100, 1 },
            new int[] { 8500, 0, 0, 100, 1 },
            new int[] { 9000, 35, 100, 100, 1 },
            new int[] { 12000, 0, 0, 100, 1 },
            new int[] { 14000, 0, 0, 100, 0 }});
        writeRoute(new Point3f(-155.5f, 30.0f, 2.0f),
                new Vector3f(97.0f, 0.0f, 0.0f),
                movements, 8,
                false,
                dataOutputStream);
        // ROUTE 5.
        movements = new ArrayList<int[][]>();
        movements.add(new int[][] {
            new int[] { 1700, 0, 0, 100, 1 },
            new int[] { 3000, 0, -100, 100, 1 },
            new int[] { 4000, 0, 0, 100, 1 },
            new int[] { 7000, 0, 100, 100, 1 },
            new int[] { 8500, 0, 0, 100, 1 },
            new int[] { 9000, -70, 100, 100, 1 },
            new int[] { 10200, 0, 100, 100, 1 },
            new int[] { 12000, 0, 0, 100, 1 },
            new int[] { 13500, 0, 0, 100, 0 }});
        writeRoute(new Point3f(-157.0f, 30.0f, -3.0f),
                new Vector3f(97.0f, 0.0f, 0.0f),
                movements, 9,
                false,
                dataOutputStream);
        // ROUTE 6.
        movements = new ArrayList<int[][]>();
        movements.add(new int[][] {
            new int[] { 1700, 0, 0, 100, 1 },
            new int[] { 3000, 0, -100, 100, 1 },
            new int[] { 4000, 0, 0, 100, 1 },
            new int[] { 7000, 0, 100, 100, 1 },
            new int[] { 8500, 0, 0, 100, 1 },
            new int[] { 9000, 70, 100, 100, 1 },
            new int[] { 10200, 0, 100, 100, 1 },
            new int[] { 12000, 0, 0, 100, 1 },
            new int[] { 13500, 0, 0, 100, 0 }});
        writeRoute(new Point3f(-157.0f, 30.0f, 3.0f),
                new Vector3f(97.0f, 0.0f, 0.0f),
                movements, 9,
                false,
                dataOutputStream);
        // Writes 'needed score'.
        dataOutputStream.writeInt(39000);
        // Writes 'duration'.
        dataOutputStream.writeChar(20);

        // Writes number of radio sounds.
        dataOutputStream.writeChar(4);

        // Radio sounds.
        dataOutputStream.writeChar(0);
        dataOutputStream.writeInt(350);
        // -
        dataOutputStream.writeChar(8);
        dataOutputStream.writeInt(2600);
        // -
        dataOutputStream.writeChar(7);
        dataOutputStream.writeInt(7600);
        // -
        dataOutputStream.writeChar(3);
        dataOutputStream.writeInt(13500);
    }

    /**
     * Creates a stick movement.
     * @param startTime The start time.
     * @param deltaTime The time between each movement.
     * @param startX The start x.
     * @param deltaX The x variation of each movement.
     * @param startY The start y.
     * @param deltaY The y variation of each movement.
     * @param count The amount of movement.
     * @param enginePower The engine power (Will not vary).
     * @param smokeOn The smoke flag.
     * @return The movements.
     */
    private static int[][] createMovement(int startTime, int deltaTime, int startX, int deltaX,
            int startY, int deltaY, int count, int enginePower, boolean smokeOn)
    {
        int[][] movements = new int[count][5];
        for (int i = 0; i < movements.length; ++i)
        {
            movements[i][0] = startTime + i * deltaTime;
            movements[i][1] = startX + i * deltaX;
            movements[i][2] = startY + i * deltaY;
            movements[i][3] = enginePower;
            movements[i][4] = smokeOn == true ? 1 : 0;
        }
        return movements;
    }

    /**
     * Writes an airplane route.
     * @param position The start position.
     * @param speed The start speed.
     * @param movements The movements.
     * @param movementsCount The number of movement.
     * @param inverted Inverted flag.
     * @param dataOutputStream Output stream.
     * @throws IOException Thrown if an I/O exception occurs.
     */
    private static void writeRoute(Point3f position,  Vector3f speed, List<int[][]> movements,
            int movementsCount, boolean inverted, DataOutputStream dataOutputStream) throws IOException
    {
        // Writes 'x y z'.
        dataOutputStream.writeFloat(position.x);
        dataOutputStream.writeFloat(position.y);
        dataOutputStream.writeFloat(position.z);
        // Writes 'initial x y z speed vector'.
        dataOutputStream.writeFloat(speed.x);
        dataOutputStream.writeFloat(speed.y);
        dataOutputStream.writeFloat(speed.z);
        // Writes 'k' movements plus the first movement.
        dataOutputStream.writeChar(movementsCount + 1);
        // Writes the first movement.
        dataOutputStream.writeInt(0);
        dataOutputStream.writeByte(0);
        dataOutputStream.writeByte(0);
        dataOutputStream.writeByte(100);
        dataOutputStream.writeBoolean(false);
        // Writes the other movements.
        for (int[][] movementArray : movements)
        {
            for (int[] movement : movementArray)
            {
                // Time.
                dataOutputStream.writeInt(movement[0]);
                // Axis x.
                dataOutputStream.writeByte(movement[1]);
                // Axis y.
                dataOutputStream.writeByte(movement[2]);
                // Engine power.
                dataOutputStream.writeByte(movement[3]);
                // Smoke button.
                dataOutputStream.writeBoolean(movement[4] == 1);
            }
        }
        dataOutputStream.writeBoolean(inverted);
    }
}
